package com.unknow.first.dapp.admin.account.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.lianziyou.chain.common.api.CommonPage;
import com.unknow.first.dapp.admin.account.config.AddressAccountProperties;
import com.unknow.first.dapp.admin.account.constants.AccountConstants.AccountStatus;
import com.unknow.first.dapp.admin.account.mapper.*;
import com.unknow.first.dapp.admin.account.service.AddressAccountDetailService;
import com.unknow.first.dapp.admin.account.service.AddressAccountInfoService;
import com.unknow.first.dapp.admin.account.service.AddressCoinConfigService;
import com.unknow.first.dapp.admin.account.service.WithdrawAroundInterface;
import com.unknow.first.dapp.admin.account.utils.Web3Utils;
import com.unknow.first.dapp.admin.account.vo.ChargeVO;
import lombok.extern.slf4j.Slf4j;
import org.cloud.common.service.AESService;
import org.cloud.core.redis.RedisUtil;
import org.cloud.exception.BusinessException;
import org.cloud.utils.SpringContextUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Sign;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.unknow.first.dapp.admin.account.constants.AccountConstants.AddressAccountError.ERROR_CHARGE_IS_NOT_ENOUGH;

/**
 * @author Administrator
 * @description 针对表【t_address_account_detail】的数据库操作Service实现
 * @createDate 2022-05-17 10:21:36
 */
@Slf4j
@Service
public class AddressAccountDetailServiceImpl extends ServiceImpl<AddressAccountDetailMapper, AddressAccountDetail> implements AddressAccountDetailService {

    @Autowired
    AddressAccountInfoMapper addressAccountInfoMapper;

    @Autowired
    AddressAccountInfoService accountInfoService;

    @Autowired
    AddressCoinConfigMapper addressCoinConfigMapper;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    AESService aesService;

    @Autowired
    AddressAccountInfoService addressAccountInfoService;

    @Autowired
    AddressCoinConfigService addressCoinConfigService;

    @Autowired
    AddressAccountProperties addressAccountProperties;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int createTrade(AddressAccountDetail accountDetail) throws Exception {
        Assert.isTrue(accountDetail.getAmount().equals(BigDecimal.ZERO), "addressAccount.error.tradeAmountZero");
        int updateCount = addressAccountInfoMapper.updateBalanceByAccountDetail(accountDetail);  // 创建或者更新余额
        Assert.isTrue(updateCount > 0, "addressAccount.error.noDataUpdated");
        return this.getBaseMapper().insert(accountDetail); // 增加交易记录
    }


//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    @AuthLog(bizType = "addressAccount.tcWithdraw", desc = "用户提现")
//    public AddressAccountDetail createWithdraw(final String toAddress, Integer coinId, final BigDecimal originAmount) throws Exception {
//        String address = RequestContextManager.single().getRequestContext().getUser().getUsername();
//        //处理器1
//        WithdrawHandlerInterface withdrawHandlerInterface = SpringContextUtil.getBean(WithdrawHandlerInterface.class);
//        if (withdrawHandlerInterface != null){
//            withdrawHandlerInterface.doBefore(address, coinId, originAmount);
//        }
////        处理器2
//        WithdrawAroundInterface withdrawAroundBean = this.getWithdrawAroundBean(coinId);
//        BigDecimal realAmount = originAmount;
//        if (withdrawAroundBean != null) {
//            realAmount = withdrawAroundBean.doBefore(address, coinId, originAmount);
//        }
//        AddressAccountInfo addressAccountInfo = addressAccountInfoService.getAddressAccountByCoinId(address, coinId);
//        Assert.notNull(addressAccountInfo, "AddressAccount.error.accountIsNotExists");  // 账户不存在或者被禁用
//        Assert.isTrue(addressAccountInfo.getStatus() == AccountStatus.NORMAL.status, "AddressAccount.error.accountIsDisabled");  // 账户不存在或者被禁用
//        final BigDecimal addressBalance = addressAccountInfo.getBalance();
//        if (addressBalance.compareTo(BigDecimal.ZERO) < 1) {
//            throw new Exception(String.format("地址：[%s]币种：[%d]余额为0", address, coinId));
//        }
//        // 防止js的16位精度问题，如果提现额大于当前余额，那么按当前余额全部提现
//        if (realAmount.compareTo(addressBalance) > 0) {
//            log.warn("[{}]用户提现-[, 原始提现金额:{}, 实际提现金额:{},超出用户余额{}]，按用户余额全部提现！", address, originAmount, realAmount, addressBalance);
//            realAmount = addressBalance;
//        }
//        QueryWrapper<AddressCoinConfig> coinConfigQueryWrapper = new QueryWrapper<>();
//        coinConfigQueryWrapper.eq("coin_id", coinId);
//        coinConfigQueryWrapper.in("status", ONLY_WITHDRAW.status, NORMAL.status);
//        AddressCoinConfig addressCoinConfig = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
//        Assert.isTrue(addressCoinConfig != null, "addressAccount.error.coinIdNoExistsOrCanNotWithDraw");
//        // 查询当前币种信息
//        JSONObject withdrawJSON = new JSONObject();
//        withdrawJSON.put(TradeRemarkColumn.PAY_TO.code, toAddress);  // 记录转出地址,方便提现审核时的签名
//
//        TradeStatus tradeStatus = TradeStatus.AUDITING;
//        if (realAmount.compareTo(addressCoinConfig.getWithdrawAuthLimit()) < 0) {
//            tradeStatus = TradeStatus.UNCONFIRMED;
//        }
//        ChargeVO chargeAmountVO = addressCoinConfigService.estimatedCharge(coinId, realAmount);
//        AddressAccountDetail addressAccountDetail;
//        if (TradeStatus.UNCONFIRMED.equals(tradeStatus)) {
//            if (addressCoinConfig.getCoinId() == addressCoinConfig.getChargeCoinId()) {
//                JSONObject signOrderDataJSON = this.signOrderData(toAddress, realAmount.subtract(chargeAmountVO.getCharge()), addressCoinConfig);
//                signOrderDataJSON.put(TradeRemarkColumn.PAY_TO.code, toAddress);
//                addressAccountDetail = addressAccountInfoService.processFundAndDetail(BigDecimal.ZERO.subtract(realAmount).add(chargeAmountVO.getCharge()),
//                    addressCoinConfig, TradeType.WITHDRAW, signOrderDataJSON.toJSONString(), -1L, address, address, tradeStatus.value);
//            } else {
//                checkChargeAmount(chargeAmountVO, address);
//                addressAccountDetail = addressAccountInfoService.processFundAndDetail(BigDecimal.ZERO.subtract(realAmount), addressCoinConfig,
//                    TradeType.WITHDRAW, withdrawJSON.toJSONString(), -1L, address, address, tradeStatus.value);
//            }
//            if (chargeAmountVO.getCharge().compareTo(BigDecimal.ZERO) > 0) {
//                //创建手续费订单
//                coinConfigQueryWrapper = new QueryWrapper<>();
//                coinConfigQueryWrapper.eq("coin_id", addressCoinConfig.getChargeCoinId());
//                AddressCoinConfig addressCoinConfigCharge = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
//
//                String chargeAccount = SystemAddressUtil.single().getFirstSystemAddress(SystemAddressType.SERVICE_CHARGE);
//                JSONObject chargeJsonObject = new JSONObject();
//                chargeJsonObject.put(TradeRemarkColumn.PAY_TO.code, chargeAccount);
//                addressAccountInfoService.processFundAndDetail(BigDecimal.ZERO.subtract(chargeAmountVO.getCharge()), addressCoinConfigCharge,
//                    TradeType.WITHDRAW_CHARGE, chargeJsonObject.toJSONString(), addressAccountDetail.getReferId(), address, address, TradeStatus.DONE.value);
//
//                chargeJsonObject.remove(TradeRemarkColumn.PAY_TO.code);
//                chargeJsonObject.put(TradeRemarkColumn.PAY_BY.code, address);
//                addressAccountInfoService.processFundAndDetail(chargeAmountVO.getCharge(), addressCoinConfigCharge, TradeType.WITHDRAW_CHARGE,
//                    chargeJsonObject.toJSONString(), addressAccountDetail.getReferId(), chargeAccount, address, TradeStatus.DONE.value);
//            }
//        } else {
//            addressAccountDetail = addressAccountInfoService.processFundAndDetail(BigDecimal.ZERO.subtract(realAmount), addressCoinConfig, TradeType.WITHDRAW,
//                withdrawJSON.toJSONString(), -1L, address, address, tradeStatus.value);
//        }
//        if (withdrawAroundBean != null) {
//            withdrawAroundBean.doAfter(address, coinId, originAmount, realAmount);
//        }
//        if (withdrawHandlerInterface != null){
//            withdrawHandlerInterface.doAfter(addressAccountDetail);
//        }
//        // 创建订单
//        return addressAccountDetail;
//    }

    public void checkChargeAmount(ChargeVO chargeAmountVO, final String address) throws BusinessException {
        AddressAccountInfo addressAccountInfoQuery = new AddressAccountInfo();
        addressAccountInfoQuery.setAddress(address);
        addressAccountInfoQuery.setCoinId(chargeAmountVO.getCoinId());
        addressAccountInfoQuery.setStatus(AccountStatus.NORMAL.status);
        QueryWrapper<AddressAccountInfo> queryWrapper = Wrappers.query(addressAccountInfoQuery);
        queryWrapper.select("sum(balance) as balance");
        AddressAccountInfo addressAccountInfo = addressAccountInfoMapper.selectOne(queryWrapper);
        if (addressAccountInfo == null || addressAccountInfo.getBalance().compareTo(chargeAmountVO.getCharge()) < 0) {
            throw new BusinessException(ERROR_CHARGE_IS_NOT_ENOUGH.i18n);
        }
    }

//    @Override
//    public void processWithdrawOrderStatusSuccess() throws IOException {
//        long currentTimeMillis = System.currentTimeMillis();
//        //五分钟前的时间
//        Date date = new Date(currentTimeMillis - 1000 * 60 * 5);
//
//        LambdaQueryWrapper<AddressAccountDetail> queryWrapper = Wrappers.lambdaQuery(AddressAccountDetail.class)
//            .eq(AddressAccountDetail::getTradeType, TradeType.WITHDRAW.value).eq(AddressAccountDetail::getStatus, TradeStatus.UNCONFIRMED.value)
//            .lt(AddressAccountDetail::getUpdatedDate, date)    //五分钟前通过审核的提现订单
//            .last("limit 50");
//        List<AddressAccountDetail> list = getBaseMapper().selectList(queryWrapper);
//        if (CollectionUtils.isEmpty(list)) {
//            return;
//        }
//        final List<String> rpcUrl = addressAccountProperties.getRpcUrls();
//        Web3j web3j = Web3j.build(new HttpService(rpcUrl.get(new SecureRandom().nextInt(rpcUrl.size()))));
//
//        for (AddressAccountDetail accountDetail : list) {
//            if (ObjectUtils.isEmpty(accountDetail.getOrderNo())) {
//                continue;
//            }
//            String contractAddress = JSON.parseObject(accountDetail.getRemark()).getString("contractAddress");
//            boolean orderStatus = Web3Utils.single().getETHWithdrawOrderStatus(web3j, contractAddress, accountDetail.getOrderNo());
//            if (orderStatus) {
//                getBaseMapper().updateWithdrawOrderStatusSuccess(accountDetail.getAddressAccountDetailId());
//            }
//        }
//    }


    @Override
    public CommonPage<AddressAccountDetail> findListByPage(Integer page, Integer limit, String orderBy, QueryWrapper<AddressAccountDetail> queryWrapper)
        throws Exception {
        if (StringUtils.isEmpty(orderBy)) {
            PageHelper.startPage(page, limit);
        } else {
            PageHelper.startPage(page, limit, orderBy);
        }
        List<AddressAccountDetail> list = baseMapper.selectList(queryWrapper);
        return CommonPage.restPage(list);
    }

    @Override
    public CommonPage<AddressAccountDetail> findListByPageHistory(Integer page, Integer limit, String orderBy, Map<String, Object> queryParams)
        throws Exception {
        if (StringUtils.isEmpty(orderBy)) {
            PageHelper.startPage(page, limit);
        } else {
            PageHelper.startPage(page, limit, orderBy);
        }
        List<AddressAccountDetail> list = baseMapper.queryAccountDetailHistory(queryParams);
        return CommonPage.restPage(list);
    }


    @NotNull
    @Override
    public JSONObject signOrderData(String address, BigDecimal amount, AddressCoinConfig addressCoinConfig) throws Exception {
        final List<String> rpcUrl = addressAccountProperties.getRpcUrls();
        Web3j web3j = Web3j.build(new HttpService(rpcUrl.get(new SecureRandom().nextInt(rpcUrl.size()))));
        long chainId = Web3Utils.single().getChainId(web3j);
        String contractAddress = addressCoinConfig.getPayContract();
        String orderNo = Hash.sha3String(Numeric.toHexString(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)));
        BigInteger bigAmount = amount.multiply(BigDecimal.TEN.pow(addressCoinConfig.getCoinDecimals() == null ? 18 : addressCoinConfig.getCoinDecimals()))
            .toBigInteger();
        String messageStr =
            "0x" + Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(contractAddress.substring(2), 16), 64) + Numeric.toHexStringNoPrefixZeroPadded(
                new BigInteger(address.substring(2), 16), 64)
                + orderNo.substring(2) + Numeric.toHexStringNoPrefixZeroPadded(bigAmount, 64) + Numeric.toHexStringNoPrefixZeroPadded(
                BigInteger.valueOf(chainId), 64);
        Credentials credentials = Credentials.create(aesService.decrypt(addressAccountProperties.getWithdrawPrivateKey()));
        Sign.SignatureData signatureData = Sign.signMessage(Numeric.hexStringToByteArray(messageStr), credentials.getEcKeyPair());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("contractAddress", contractAddress);
        jsonObject.put("orderNo", orderNo);
        jsonObject.put("amount", bigAmount.toString());
        jsonObject.put("v", Numeric.toBigInt(signatureData.getV()));
        jsonObject.put("r", Numeric.toHexString(signatureData.getR()));
        jsonObject.put("s", Numeric.toHexString(signatureData.getS()));
        return jsonObject;
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    @AuthLog(bizType = "addressAccount.authWithdraw", desc = "用户提现审核")
//    public int authWithdraw(Long addressAccountDetailId, @NotNull Boolean isPass, String reason) throws Exception {
//        LoginUserDetails loginUserDetails = RequestContextManager.single().getRequestContext().getUser();
//        AddressAccountDetail addressAccountDetail = this.getBaseMapper().selectById(addressAccountDetailId);
//        Assert.isTrue(addressAccountDetail != null && TradeStatus.AUDITING.value == addressAccountDetail.getStatus(),
//            "addressAccount.error.addressAccountDetailStatusNoInWithDrawing");  // 状态不在提现审核中
//        JSONObject signOrderData = new JSONObject();
//
//        TradeStatus tradeStatus = TradeStatus.AUDIT_FAIL;
//        BigDecimal realAmount = addressAccountDetail.getAmount();
//        if (isPass) {
//            tradeStatus = TradeStatus.UNCONFIRMED;
//            final BigDecimal amount = BigDecimal.ZERO.subtract(addressAccountDetail.getAmount());
//            QueryWrapper<AddressCoinConfig> coinConfigQueryWrapper = new QueryWrapper<>();
//            coinConfigQueryWrapper.eq("coin_id", addressAccountDetail.getCoinId());
//            AddressCoinConfig addressCoinConfig = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
//            Assert.isTrue(addressCoinConfig != null, "addressAccount.error.coinIdNoExists");
//            coinConfigQueryWrapper = new QueryWrapper<>();
//            coinConfigQueryWrapper.eq("coin_id", addressCoinConfig.getChargeCoinId());
//            AddressCoinConfig addressCoinConfigCharge = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
//            //创建手续费订单
//            ChargeVO chargeAmountVO = addressCoinConfigService.estimatedCharge(addressAccountDetail.getCoinId(), amount);
//            final String toAddress = addressAccountDetail.getRemarkValue(TradeRemarkColumn.PAY_TO.code);
//            // 内扣手续费
//            if (addressCoinConfig.getChargeCoinId() == addressCoinConfig.getCoinId()) {
//                signOrderData = this.signOrderData(toAddress, amount.subtract(chargeAmountVO.getCharge()), addressCoinConfig);
//                realAmount = realAmount.add(chargeAmountVO.getCharge());// 真实提现金额要加上手续费，因为手续费另外算
//            } else {
//                checkChargeAmount(chargeAmountVO, addressAccountDetail.getAddress());
//                signOrderData = this.signOrderData(toAddress, amount, addressCoinConfig);
//            }
//            signOrderData.put(TradeRemarkColumn.PAY_TO.code, toAddress);
//            if (chargeAmountVO.getCharge().compareTo(BigDecimal.ZERO) > 0) {
//                //创建手续费订单
//                String chargeAccount = SystemAddressUtil.single().getFirstSystemAddress(SystemAddressType.SERVICE_CHARGE);
//                JSONObject remarkObject = new JSONObject();
//                remarkObject.put(TradeRemarkColumn.PAY_TO.code, chargeAccount);
//                addressAccountInfoService.processFundAndDetail(BigDecimal.ZERO.subtract(chargeAmountVO.getCharge()), addressCoinConfigCharge,
//                    TradeType.WITHDRAW_CHARGE, remarkObject.toJSONString(), addressAccountDetail.getAddressAccountDetailId(), addressAccountDetail.getAddress(),
//                    loginUserDetails.getUsername(), TradeStatus.DONE.value);
//                remarkObject.remove(TradeRemarkColumn.PAY_TO.code);
//                remarkObject.put(TradeRemarkColumn.PAY_BY.code, addressAccountDetail.getAddress());
//                addressAccountInfoService.processFundAndDetail(chargeAmountVO.getCharge(), addressCoinConfigCharge, TradeType.WITHDRAW_CHARGE,
//                    remarkObject.toJSONString(), addressAccountDetail.getReferId(), chargeAccount, addressAccountDetail.getAddress(), TradeStatus.DONE.value);
//            }
//        } else {
//            signOrderData.put("authNoPassReason", reason);
//        }
//
//        // 更新资金明细信息
//        QueryWrapper<AddressAccountDetail> addressAccountDetailUpdateWrapper = new QueryWrapper<>();
//        addressAccountDetailUpdateWrapper.eq("address_account_detail_id", addressAccountDetail.getAddressAccountDetailId());
//        addressAccountDetailUpdateWrapper.eq("status", TradeStatus.AUDITING.value);
//        AddressAccountDetail addressAccountDetailUpdate = new AddressAccountDetail();
//        addressAccountDetailUpdate.setRemark(signOrderData.toJSONString());
//        addressAccountDetailUpdate.setStatus(tradeStatus.value);
//        addressAccountDetailUpdate.setAmount(realAmount);
//        addressAccountDetailUpdate.setUpdatedBy(loginUserDetails.getUsername());
//        int updateAccountDetailCount = this.getBaseMapper().update(addressAccountDetailUpdate, addressAccountDetailUpdateWrapper);
//        Assert.isTrue(updateAccountDetailCount > 0, "addressAccount.error.addressAccountDetail.AuthWithdrawUpdateError");  // 更新失败
//
//        // 更新账户相关信息,仅限审核通过时处理
//        if (isPass) {
//            addressAccountDetail.setAmount(realAmount);
//            int updateAccountInfoCount = addressAccountInfoMapper.updateBalanceByAccountDetail(addressAccountDetail);
//            Assert.isTrue(updateAccountInfoCount > 0, "addressAccount.error.addressAccountDetail.AuthWithdrawUpdateAccountInfoError");  // 更新失败
//        }
//
//        return updateAccountDetailCount;
//    }

    private WithdrawAroundInterface getWithdrawAroundBean(Integer coinId) {
        try {
            String beanName = addressCoinConfigMapper.getAroundProcessBean(coinId);
            if (StringUtils.hasLength(beanName)) {
                return SpringContextUtil.getBean(beanName);
            } else {
                return SpringContextUtil.getBean(WithdrawAroundInterface.WITHDRAW_AROUND_BEAN_PREFIX + coinId);
            }

        } catch (Exception e) {
            log.error("未获取到提现前后置处理BEAN");
            return SpringContextUtil.getBean(WithdrawAroundInterface.WITHDRAW_AROUND_BEAN_PREFIX + coinId);
        }
    }
}




